package IC;


import java.io.*;
import java_cup.runtime.*;
import IC.AST.*;
import IC.LIR.OptTranslationVisitor;
import IC.LIR.SettiUlmanRegisterCounterVisitor;
import IC.LIR.TranslationVisitor;
import IC.Parser.*;
import IC.SymbolTable.GlobalSymbolTable;
import IC.TypeTable.TypeTable;
import IC.Visitors.Checker;
import IC.Visitors.buildingSymbolTables;

import java.util.LinkedList;

public class Compiler{

	//Class fields
	private static String icFile, libraryPath;
	private static Program root_lib, root_file;
	private static	ICClass lc;
	private static String correctFormat= "Usage: <file.ic> -L<LibPath> -print-ast -dump-symtab -print-lir -opt-lir";

	// G:\CS\compilation\Projects\PA4\test\example1.ic -print-lir -opt-lir
	// G:\CS\compilation\Projects\PA4\test\example1.lir -verbose:2 -stats
	//C:\Users\AnnaG\java_workspace\PA4\test\example1.ic -print-lir -opt-lir
	//C:\Users\AnnaG\java_workspace\PA4\test\example1.lir -verbose:2 -stats

	private static boolean print_ast=false;
	private static boolean dump_symtab = false;
	private static boolean printlir_flag = false;
	private static boolean optlir_flag = false;
	private static boolean is_Library=false; //TODO

	//Parsing the arguments and  checking arguments
	public static void parseArgs(String[] args){

		if (args.length == 0) {
			throw new RuntimeException("Error: No input file argument was given\n" );
		}		

		icFile = args[0];

		for (int i =1; i< args.length; i++)
		{
			String arg = args[i];
			//TODO
			if (arg.startsWith("-L")) 
			{
				is_Library = true;
				libraryPath = arg.substring(2);
			}
			else
				if (arg.equalsIgnoreCase("-print-ast")) 
				{
					print_ast = true;

				} 
				else 
					if (arg.equalsIgnoreCase("-dump-symtab")) 
					{
						dump_symtab = true;
					} 
					else 
						if (arg.equalsIgnoreCase("-print-lir")) 
						{
							printlir_flag = true;
						} 
						else 
							if (arg.equalsIgnoreCase("-opt-lir")) 
							{
								optlir_flag = true;
							} 
							else
							{
								throw new RuntimeException("Error: incorrect format. The format should be: "+ correctFormat);
							}
		}
	}	
	//////////////////////////////////////////////////////////////////////////////

	//Scanning and parsing an IC file and pretty-prints it.
	public static void main(String[] args) 	{

		try {
			parseArgs(args);

			//Parsing the library file 

			
			//TODO
			String libPath="";
			if (!(is_Library)){
				
				libraryPath = ".\\libic.sig";
				 libPath = "libic.sig";
			}
			else
			{
				 libPath = libraryPath.substring(libraryPath.lastIndexOf("\\")+1);
			}
			
			try 
			{
				FileReader libraryFile = new FileReader(libraryPath);	
				// Create a new scanner
				Lexer libraryScanner = new Lexer(libraryFile);

				LibraryParser libParser = new LibraryParser(libraryScanner);
				LinkedList<ICClass> icl= new LinkedList<ICClass>();

				Symbol slc= libParser.parse();	
				lc = (ICClass)(slc.value);

				icl.add(lc);

				root_lib= new Program(icl);		

				System.out.println("\nParsed " + libPath + " successfully!");					

			} 
			catch (IOException ex) 
			{
				System.out.println("unexpected problem (IOException) with libraly file");
				return;
			}

			catch( Exception e)  // may throw LexicalError or SyntaxError
			{
				System.out.println("Error in Library File: " + libPath );
				System.out.println(e);
				return;
			}

			//Pretty-print the library file
			if(print_ast) {
				PrettyPrinter printer = new PrettyPrinter(libraryPath);  //"libic.sig"
				if (root_lib.getClasses() != null)
				{
					//System.out.println(printer.visit(lc));
					System.out.println(root_lib.accept(printer));
				}
				else 
				{
					System.out.println(" root_file.getClasses() == null ");
				}
			}

			///////////////////////////////////////////////////////////////

			//Parsing the input IC file

			String icPath = icFile.substring(icFile.lastIndexOf("\\")+1);

			try {
				FileReader ICFile = new FileReader(icFile);
				Lexer scanner = new Lexer(ICFile);
				Parser parser = new Parser(scanner);

				root_file = (Program) parser.parse().value;

				System.out.println("Parsed " + icPath + " successfully!");

			}
			catch (IOException ex)
			{
				System.out.println("unexpected problem (IOException) with input file (IC File) ");
				return;
			}
			catch( Exception e)  // may throw LexicalError or SyntaxError
			{
				System.out.println("Error in IC File: " + icPath );
				System.out.println(e);
				return;
			}


			//Pretty-print the ic file
			if( print_ast) {				
				PrettyPrinter printer = new PrettyPrinter(icFile);
				if (root_file.getClasses() != null)
				{
					System.out.println(printer.visit(root_file));
				}
				else 
				{
					System.out.println(" root_file.getClasses() == null ");

				}
			}



			/////// semantic analysis //////////////////
			root_file.getClasses().add(0, lc);
			//System.out.println(root_file.getClasses().toString());
			buildingSymbolTables bst=new buildingSymbolTables(icFile);
			Object gst=root_file.accept(bst);
			if (gst==null)
			{System.out.println("Error has been discovered (builder)");return;}
			else


				if (dump_symtab){
					System.out.println("\n"+gst);
					System.out.println(TypeTable.typeTableToString());
				}

			Checker c=new Checker((GlobalSymbolTable)gst);
			Object semanticC=root_file.accept(c);
			if (semanticC==null)
			{System.out.println("Error has been discovered (checker)");return;}
			else {
				//TODO	System.out.println("Checker finished successfully");
			}



			/////////	LIR code translation 

			TranslationVisitor translatorVisitor ;
			String LIRCode="";
			if (printlir_flag){

				// build translating visitor 
				//GlobalSymbolTable global = (GlobalSymbolTable)gst;
				translatorVisitor = new TranslationVisitor((GlobalSymbolTable)gst);
				LIRCode=root_file.accept(translatorVisitor, 0);
				//TODO delete nextLine
				//System.out.println(LIRCode);
			}
			// num of ASTNodes registers
			if (optlir_flag){
				try{
					int SettiUlmanReg = (Integer) root_file.accept(new SettiUlmanRegisterCounterVisitor()); 
					translatorVisitor = new OptTranslationVisitor((GlobalSymbolTable)gst);
					LIRCode=root_file.accept(translatorVisitor, 0);
					/** System.out.println("SettiUlmanReg:  " + SettiUlmanReg);
				System.out.println();
				System.out.println();
					 **/
				}catch (Exception e) {

					System.out.println("Exeption in LIR optimization: " + e.getMessage());
					return;
				}
			}

			if ((printlir_flag) || (optlir_flag)){
				// insert LIR translation to file
				String lirFileName = args[0].substring(0,icFile.length()-2)+"lir";
				try {
					BufferedWriter buff = new BufferedWriter(new FileWriter(lirFileName));
					buff.write(LIRCode);
					buff.flush();
					buff.close();
				} catch (IOException e) {
					System.err.println("Failed writing to file: "+lirFileName);
					e.printStackTrace();
				}
				//TODO
				// System.out.println("LIR translation");
				// System.out.println("===============");
				// System.out.println(LIRCode);
				System.out.println("The program was successfully translated");
			}

		} catch (Exception e) {

			System.out.println(e.getMessage());
			return;
		}



	}
}